/*
* $Id: EditDraw.cpp 20 2010-01-19 15:40:42Z volok-aleksej@yandex.ru $
*
* Copyright 2009 by Volokitin Alexey
* All rights reserved
*
*/
#include "Common/include.h"
#include "EditDraw.h"
#include "KeyProcess.h"
#include "../EditorItem.h"

CEditDraw::CEditDraw(const std::tstring& strName, CEditorItem* pParent)
: CDrawControl(strName), m_posSel(0, 0),
m_nPosCursor(0), m_nLineCursor(0), m_pParent(pParent){
	int cx = 0;
	for(int i = 0; i < (int)m_strValue.size(); i++){
		m_aLetters.push_back(CLetterDraw(m_strValue[i]));
		if(m_strValue[i] == ' ' || m_strValue[i] == '\n'){
			m_aSepLetterPos.push_back(i);
			cx = 0;
		}else
			cx += m_aLetters.back().GetRect().Size().cx;
	}
	m_aSepLetterPos.push_back((int)m_strValue.size());

	m_aKeyProcess.push_back(new CArrawLeft(this));
	m_aKeyProcess.push_back(new CArrawRight(this));
	m_aKeyProcess.push_back(new CArrawUp(this));
	m_aKeyProcess.push_back(new CArrawDown(this));
	m_aKeyProcess.push_back(new CShiftArrawLeft(this));
	m_aKeyProcess.push_back(new CShiftArrawRight(this));
	m_aKeyProcess.push_back(new CShiftArrawUp(this));
	m_aKeyProcess.push_back(new CShiftArrawDown(this));
	m_aKeyProcess.push_back(new CBackSpace(this));
	m_aKeyProcess.push_back(new CDelete(this));
	m_aKeyProcess.push_back(new CEnter(this));
	m_aKeyProcess.push_back(new CCopy(this));
	m_aKeyProcess.push_back(new CCharKey(this));
}
CEditDraw::~CEditDraw(){}

void CEditDraw::Resize(){
	int nCx = 2, nCyLetter = 0, nCy = 2;
	m_aLineBeginPos.clear();
	m_aLineBeginPos.push_back(0);
	if(!m_aLetters.empty())
		nCyLetter = m_aLetters[0].GetRect().Size().cy;
	for(std::vector<CLetterDraw>::iterator it = m_aLetters.begin(); it != m_aLetters.end(); it++){
		it->SetBeginPoint(CPoint(m_rect.left + nCx, m_rect.top + nCy));
		int size = 0;
		if(it + 1 != m_aLetters.end())
			size = it->GetRect().Size().cx + (it + 1)->GetRect().Size().cx;
		if(nCx + size > m_rect.Size().cx ||
			m_strValue[it - m_aLetters.begin()] == '\n'){
			nCx = 2;
			nCy += nCyLetter;
			m_aLineBeginPos.push_back(int(it - m_aLetters.begin()) + 1);
		}else
			nCx += it->GetRect().Size().cx;
	}
	m_rect.bottom = m_rect.top + nCy + nCyLetter + 2;
}

void CEditDraw::PaintActive(CDC* dc){
	CBrush brush;
	brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
	dc->FillRect(m_rect, &brush);
	brush.DeleteObject();
	CPoint pos;
	if(m_posSel.x > m_posSel.y)
		pos.SetPoint(m_posSel.y, m_posSel.x);
	else
		pos.SetPoint(m_posSel.x, m_posSel.y);
	for(std::vector<CLetterDraw>::iterator it = m_aLetters.begin(); it != m_aLetters.end(); it++){
		int curpos = int(it - m_aLetters.begin());
		if(pos.x <= curpos && curpos < pos.y)
			it->SetSel(true);
		else
			it->SetSel(false);
		it->Paint(dc);
	}
	if(m_nPosCursor < (int)m_aLetters.size()){
		dc->MoveTo(m_aLetters[m_nPosCursor].GetRect().TopLeft());
		dc->LineTo(CPoint(m_aLetters[m_nPosCursor].GetRect().left, m_aLetters[m_nPosCursor].GetRect().bottom));
	}else if(m_nPosCursor && m_nPosCursor == m_aLetters.size()){
		if(m_strValue[m_nPosCursor - 1] != '\n'){
			dc->MoveTo(CPoint(m_aLetters[m_nPosCursor - 1].GetRect().right, m_aLetters[m_nPosCursor - 1].GetRect().top));
			dc->LineTo(CPoint(m_aLetters[m_nPosCursor - 1].GetRect().right, m_aLetters[m_nPosCursor - 1].GetRect().bottom));
		}else if(m_strValue[m_nPosCursor - 1] == '\n'){
			dc->MoveTo(CPoint(m_rect.left + 2, m_aLetters[m_nPosCursor - 1].GetRect().bottom));
			dc->LineTo(CPoint(m_rect.left + 2, m_aLetters[m_nPosCursor - 1].GetRect().bottom + m_aLetters[m_nPosCursor - 1].GetRect().Size().cy));
		}
	}else if(m_nPosCursor == 0){
		dc->MoveTo(CPoint(m_rect.left + 2, m_rect.top + 2));
		dc->LineTo(CPoint(m_rect.left + 2, m_rect.bottom - 2));
	}
}

void CEditDraw::PaintNonActive(CDC* dc){
	CBrush brush;
	brush.CreateSolidBrush(GetSysColor(COLOR_3DFACE));
	dc->FillRect(m_rect, &brush);
	brush.DeleteObject();
	dc->SetBkMode(TRANSPARENT);
	dc->DrawText(m_strValue.c_str(), m_rect, DT_CENTER|DT_WORDBREAK);
}

void CEditDraw::SetPos(CPoint pos){
	CPoint posRes = pos - m_rect.TopLeft();
	m_rect.MoveToXY(pos);
	for(std::vector<CLetterDraw>::iterator it = m_aLetters.begin(); it != m_aLetters.end(); it++){
		it->SetBeginPoint(it->GetRect().TopLeft() + posRes);
	}
}

CPoint CEditDraw::GetSel(){
	return m_posSel;
}
void CEditDraw::SetSel(CPoint sel){
	if(sel.y > (int)m_aLetters.size())
		sel.y = (int)m_aLetters.size();
	if(sel.y < 0)
		sel.y = 0;
	m_posSel = sel;
}

int CEditDraw::GetPosCursor(){
	return m_nPosCursor;
}
void CEditDraw::SetPosCursor(int posCursor){
	m_nPosCursor = posCursor;
	m_posSel.SetPoint(m_nPosCursor, m_nPosCursor);
}
void CEditDraw::OffsetCursor(int offset){
	if(m_nPosCursor == 0 && offset < 0 || m_nPosCursor == m_aLetters.size() && offset > 0)
		return;
	m_nPosCursor += offset;
	if(m_nPosCursor < 0)
		m_nPosCursor = 0;
	if(m_nPosCursor > (int)m_aLetters.size())
		m_nPosCursor = (int)m_aLetters.size();
	m_posSel.SetPoint(m_nPosCursor, m_nPosCursor);
}

int CEditDraw::GetOffsetLine(bool bUp){
	int nLine = 0;
	for(std::vector<int>::iterator it = m_aLineBeginPos.begin(); !m_aLineBeginPos.empty(); nLine++){
		if(m_nPosCursor < *it){
			nLine--;
			break;
		}
		it++;
		if(it == m_aLineBeginPos.end())
			break;
	}
	if(bUp && nLine){
		if(m_nPosCursor - m_aLineBeginPos[nLine] >= m_aLineBeginPos[nLine] - m_aLineBeginPos[nLine - 1])
			return m_aLineBeginPos[nLine] - m_nPosCursor - 1;
		return m_aLineBeginPos[nLine - 1] - m_aLineBeginPos[nLine];
	}else if(!bUp && nLine + 1 != m_aLineBeginPos.size()){
		if(nLine + 2 < (int)m_aLineBeginPos.size() &&  m_nPosCursor - m_aLineBeginPos[nLine] >= m_aLineBeginPos[nLine + 2] - m_aLineBeginPos[nLine + 1])
			return m_aLineBeginPos[nLine + 2] - m_nPosCursor - 1;
		else if(m_nPosCursor - m_aLineBeginPos[nLine] > (int)m_aLetters.size() - m_aLineBeginPos[nLine + 1])
			return (int)m_aLetters.size() - m_nPosCursor;
		return m_aLineBeginPos[nLine + 1] - m_aLineBeginPos[nLine];
	}
	return 0;
}

void CEditDraw::Delete(int pos){
	if(pos >= 0 && pos != m_aLetters.size()){
		m_aLetters.erase(m_aLetters.begin() + pos);
		m_strValue.erase(m_strValue.begin() + pos);
		m_pParent->ResizeItem();
	}
}
void CEditDraw::Delete(int begin, int end){
	int nBegin = begin, nEnd = end;
	if(nBegin > nEnd){
		nBegin = end;
		nEnd = begin;
	}
	if(nEnd > (int)m_aLetters.size())
		nEnd = (int)m_aLetters.size() - 1;
	if(nBegin >= 0){
		m_aLetters.erase(m_aLetters.begin() + nBegin, m_aLetters.begin() + nEnd);
		m_strValue.erase(m_strValue.begin() + nBegin, m_strValue.begin() + nEnd);
		m_pParent->ResizeItem();
	}
	if(begin <= end)
		OffsetCursor(begin - end);
	else
		OffsetCursor(0);
}

void CEditDraw::AddChar(int nChar){
	CPoint nCurSel = GetSel();
	if(nCurSel.x != nCurSel.y)
		CEditDraw::Delete(nCurSel.x, nCurSel.y);
	m_strValue.insert(m_nPosCursor, std::tstring((TCHAR*)&nChar, 1));
	m_aLetters.insert(m_aLetters.begin() + m_nPosCursor, CLetterDraw(nChar));
	OffsetCursor(1);
	m_pParent->ResizeItem();
}

bool CEditDraw::IsChar(int nChar){
	return nChar >= 31 || nChar == 10;
}

std::tstring CEditDraw::GetNewString(std::tstring strAddString){
	std::tstring strValue = m_strValue;
	CPoint posSel = GetSel();
	if(posSel.x > posSel.y){
		int temp = posSel.x;
		posSel.x = posSel.y;
		posSel.y = temp;
	}
	strValue.erase(strValue.begin() + posSel.x, strValue.begin() + posSel.y);
	strValue.insert(posSel.x, strAddString);
	return strValue;
}

CNumberEditDraw::CNumberEditDraw(const std::tstring& strName, CEditorItem* pParent)
: CEditDraw(strName, pParent){
	if(m_strValue.empty()){
		m_strValue.push_back('0');
		m_aLetters.push_back(CLetterDraw('0'));
	}
}
CNumberEditDraw::~CNumberEditDraw(){}

bool CNumberEditDraw::IsChar(int nChar){
	return nChar >= '0' && nChar <= '9';
}

void CNumberEditDraw::AddChar(int nChar){
	std::tstring strValue = m_strValue;
	strValue.insert(m_nPosCursor, std::tstring((TCHAR*)&nChar, 1));
	if(m_strValue.size() == 1 && m_strValue[0] == '0'){
		__super::Delete(0);
		OffsetCursor(-1);
		strValue = m_strValue;
	}
	if(strValue.size() > 9 || (m_posSel.y == 0 || m_posSel.x == 0) && nChar == '0' && m_strValue.size())
		return;
	__super::AddChar(nChar);
}
void CNumberEditDraw::Delete(int pos){
	if(Null(pos))
		__super::Delete(pos);
}
void CNumberEditDraw::Delete(int begin, int end){
	if(Null(-1))
		__super::Delete(begin, end);
}

bool CNumberEditDraw::Null(int pos){
	std::tstring strValue = m_strValue;
	if(pos == -1){
		CPoint nCurSel = GetSel();
		if(nCurSel.x > nCurSel.y){
			int temp = nCurSel.x;
			nCurSel.x = nCurSel.y;
			nCurSel.y = temp;
		}
		strValue.erase(strValue.begin() + nCurSel.x, strValue.begin() + nCurSel.y);
	}else
		strValue.erase(pos, 1);
	if(!_ttoi(strValue.c_str())){
		m_aLetters.clear();
		m_strValue = _T("0");
		m_aLetters.push_back(CLetterDraw('0'));
		m_nPosCursor = 0;
		m_posSel.SetPoint(0,0);
		Resize();
		return false;
	}
	return true;
}
CDoubleEditDraw::CDoubleEditDraw(const std::tstring& strName, CEditorItem* pParent)
: CEditDraw(strName, pParent){
	if(m_strValue.empty()){
		m_strValue.push_back('0');
		m_aLetters.push_back(CLetterDraw('0'));
	}
}
CDoubleEditDraw::~CDoubleEditDraw(){}

bool CDoubleEditDraw::IsChar(int nChar){
	return nChar >= '0' && nChar <= '9' || nChar == '.';
}

void CDoubleEditDraw::AddChar(int nChar){
	std::tstring strNewString = GetNewString(std::tstring((TCHAR*)&nChar, 1));
	if(CCommonFunction::ValidRegex(strNewString, _T("^\\d*\\.\\d*\\.\\d*$")))
		return;
	if(strNewString.size() > 2 && strNewString[0] == '0' && strNewString[2] == '.' && m_strValue[0] == '0'){
		m_strValue.erase(m_strValue.begin());
		m_aLetters.erase(m_aLetters.begin());
		OffsetCursor(-1);
		__super::AddChar(nChar);
	}else if(strNewString.size() > 1 && strNewString[0] == '.'){
		__super::AddChar(nChar);
		m_strValue.insert(m_strValue.begin(), '0');
		m_aLetters.insert(m_aLetters.begin(), CLetterDraw('0'));
		OffsetCursor(1);
	}else if(CCommonFunction::ValidRegex(strNewString, _T("^0$|^0?\\.\\d*$|^[1-9]\\d*\\.?\\d*$"))){
		__super::AddChar(nChar);
	}
}

void CDoubleEditDraw::Delete(int pos){
	if(Null(pos))
		__super::Delete(pos);
}

void CDoubleEditDraw::Delete(int begin, int end){
	if(Null(-1))
		__super::Delete(begin, end);
}

bool CDoubleEditDraw::Null(int pos){
	std::tstring strValue = m_strValue;
	if(pos == -1){
		CPoint sel = GetSel();
		if(sel.x > sel.y)
			strValue.erase(strValue.begin() + sel.y, strValue.begin() + sel.x);	
		else
			strValue.erase(strValue.begin() + sel.x, strValue.begin() + sel.y);
	}else
		strValue.erase(strValue.begin() + pos);
	size_t pos1 = strValue.find('.');
	std::tstring strWhole, strDecimal;
	if(pos1 != -1){
		strWhole.insert(strWhole.end(), strValue.begin(), strValue.begin() + pos1);
		strDecimal.insert(strDecimal.end(), strValue.begin() + pos1 + 1, strValue.end());
	}else
		strWhole = strValue;
	if(pos1 == -1 && !_ttoi(strWhole.c_str())){
		m_strValue = _T("0");
		m_aLetters.clear();
		m_aLetters.push_back(CLetterDraw('0'));
		m_nPosCursor = 0;
		m_posSel.SetPoint(0,0);
		Resize();
		return false;
	}else if(pos1 == -1 && strValue[0] == '0' ||
			pos1 > 1 && strValue[0] == '0' || pos1 == 0){
		CPoint sel = GetSel();
		if(sel.x == sel.y)
			__super::Delete(pos);
		else if(sel.x > sel.y)
			__super::Delete(sel.y, sel.x);
		else
			__super::Delete(sel.x, sel.y);
		if(pos1 == -1 && strValue[0] == '0'){
			while(true){
				if(m_strValue[0] != '0')
					break;
				m_strValue.erase(m_strValue.begin());
				m_aLetters.erase(m_aLetters.begin());
			}
			m_nPosCursor = 0;
			m_posSel.SetPoint(0,0);
		}else if(pos1 > 1 && strValue[0] == '0'){
			while(true){
				if(m_strValue[1] != '0')
					break;
				m_strValue.erase(m_strValue.begin());
				m_aLetters.erase(m_aLetters.begin());
			}
		}else if(pos1 == 0){
			m_strValue.insert(m_strValue.begin(), '0');
			m_aLetters.insert(m_aLetters.begin(), CLetterDraw('0'));
			if(pos != -1)
				OffsetCursor(1);
		}
		Resize();
		return false;
	}
	return true;
}
